﻿using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using Portable.Text;
using Sino.WebApi.Framework.Abstractions.Cryptography;
using System;
using System.Threading.Tasks;

namespace Sino.CapacityCloud.Services
{

    public struct TokenCache
    {
        public DateTime Time { get; set; }

        public string Value { get; set; }

    }

    public class TokenGenertationService
        : ConfigurableServiceBase, ITokenGenertationService
    {

        private readonly ITokenGenerator _tokenGenerator;
        private readonly IDistributedCache _cache;

        public TimeSpan Expire { get; set; }

        public TokenGenertationService(IHostingEnvironment env, ITokenGenerator tokenGenerator, IDistributedCache cache)
            : base(env, section: "AppSettings:TokenGenertationStrategy")
        {
            this._tokenGenerator = tokenGenerator;
            TimeSpan expire;
            if (!TimeSpan.TryParse(this.Configuration["Expire"], out expire))
            {
                expire = TimeSpan.FromHours(24);
            }
            this.Expire = expire;
            this._tokenGenerator.Expire = expire;
            this._cache = cache;
        }

        public async Task<string> GenerateAsync()
        {
            return await this.GenerateAsync(DateTime.Now);
        }

        public async Task<string> GenerateAsync(DateTime time)
        {
            return await this.GenerateAsync(time, null);
        }

        public async Task<string> GenerateAsync(DateTime time, string oldToken)
        {
            return await Task.Run(() =>
            {
                var token = this._tokenGenerator.Generate(time);
                var value = JsonConvert.SerializeObject(new TokenCache()
                {
                    Time = time,
                    Value = token
                });
                if (!string.IsNullOrEmpty(token))
                {
                    this._cache.RemoveAsync(token);
                }
                this._cache.Set(token, Encoding.UTF8.GetBytes(value), new DistributedCacheEntryOptions()
                {
                    AbsoluteExpiration = time + this.Expire
                });
                return token;
            });
        }

        public async Task<bool> ValidateAsync(string token)
        {
            if (this._tokenGenerator.ValidateToken(token))
            {
                var tokenData = await this._cache.GetAsync(token);
                if (tokenData != null)
                {
                    var cache = JsonConvert.DeserializeObject<TokenCache>(Encoding.UTF8.GetString(tokenData));
                    return cache.Value == token && DateTime.Now < (cache.Time + this.Expire);
                }
            }
            return false;
        }

        public async Task<DateTime> ParseCreationTimeAsync(string token)
        {
            return await Task.FromResult(this._tokenGenerator.ExtractCreationTime(token));
        }

    }

}